/*******************************************************************************
*
*	模块名称 :  dma
*
*******************************************************************************/
#include "bsp.h"
#include "bsp_dma.h"

/*******************************************************************************
*	本地变量
*******************************************************************************/
static void (*isr_callback[PDMA_CH_MAX])(int err);
static char used[PDMA_CH_MAX] = {0};

/*******************************************************************************
*	ISR
*******************************************************************************/
void PDMA_IRQHandler(void)
{
	int index;
	
	// ch0和ch1, 超时中断 - 写1清除
	if(PDMA->INTSTS & PDMA_INTSTS_REQTOF0_Msk)
	{
		PDMA->INTSTS = PDMA_INTSTS_REQTOF0_Msk;
		if(isr_callback[0])
		{
			isr_callback[0]( 3 );
		}
	}
	
	if(PDMA->INTSTS & PDMA_INTSTS_REQTOF1_Msk)
	{
		PDMA->INTSTS = PDMA_INTSTS_REQTOF1_Msk;
		if(isr_callback[1])
		{
			isr_callback[1]( 3 );
		}
	}
	
	// 其余中断
	for(index = 0; index < PDMA_CH_MAX; index++)
	{
		// 传输完成
		if(PDMA->TDSTS & (1UL << index))
		{
			PDMA->TDSTS = (1UL << index);			
			if(isr_callback[index])
			{
				isr_callback[index]( 0 );
			}
		}
		
		// 地址未对齐
		if(PDMA->ALIGN & (1UL << index))
		{
			PDMA->ALIGN = (1UL << index);		
			if(isr_callback[index])
			{
				isr_callback[index]( 1 );
			}

		}
		
		// 中止错误
		if(PDMA->ABTSTS & (1UL << index))
		{
			PDMA->ABTSTS = (1UL << index);
			
			if(isr_callback[index])
			{
				isr_callback[index]( 2 );
			}
		}
	}
}

/*******************************************************************************
*	td sts
*******************************************************************************/
void bsp_pdma_init(void)
{
	memset(used, 0, sizeof(used));
	memset(isr_callback, 0, sizeof(isr_callback));
	nvic_irq_set(PDMA_IRQn, 3, 1);
}

/*******************************************************************************
*	td sts
*******************************************************************************/
uint8_t bsp_pdma_td_state(char ch)
{
	if(PDMA->TDSTS & (1UL < ch))
	{
		return 1;
	}
	
	return 0;
}

/*******************************************************************************
*	err
*******************************************************************************/
uint8_t bsp_pdma_err_get(char ch)
{
	// 中止错误
	if(PDMA->ABTSTS & (1UL < ch))
	{
		return 1;
	}
	
	// 地址对齐错误
	if(PDMA->ALIGN & (1UL < ch))
	{
		return 2;
	}
	
	// ch0 超时
	if(ch == 0)
	{
		if(PDMA->INTSTS & PDMA_INTSTS_REQTOF0_Msk)
		{
			return 3;
		}
	}
	
	// ch1 超时
	if(ch == 1)
	{
		if(PDMA->INTSTS & PDMA_INTSTS_REQTOF1_Msk)
		{
			return 3;
		}
	}
	
	return 0;
}

/*******************************************************************************
*	int status clr
*******************************************************************************/
uint8_t bsp_pdma_intsts_clr(char ch)
{
	uint32_t msk = 1ul << ch;
	
	// 完成 中止 地址对齐
	PDMA->TDSTS = msk;
	PDMA->ABTSTS = msk;
	PDMA->ALIGN = msk;
	
	// 超时
	if(ch == 0)
		PDMA->INTSTS = PDMA_INTSTS_REQTOF0_Msk;
	if(ch == 1)
		PDMA->INTSTS = PDMA_INTSTS_REQTOF1_Msk;
	
	return 0;
}

/*******************************************************************************
*	int enable or disable
*******************************************************************************/
void bsp_pdma_int_en(char ch, char en)
{
	intx_alloc();
	
	intx_disable();
	
	if(en)
		PDMA->INTEN |= 1UL << ch;
	else
		PDMA->INTEN &= ~(1UL << ch);
	
	intx_enable();
}

/*******************************************************************************
*	int enable or disable
*******************************************************************************/
void bsp_pdma_prio(char ch, char prio)
{
	if(prio)
		PDMA->PRISET = 1UL << ch;
	else
		PDMA->PRICLR = 1UL << ch;
}

/*******************************************************************************
*	int status clr
*******************************************************************************/
void bsp_pdma_rst(char ch)
{
	intx_alloc();	
	
	intx_disable();
	
	PDMA->CHRST = 1UL << ch;
	__DSB();
	PDMA->CHRST = 0;
	__DSB();
	
	intx_enable();
}

/*******************************************************************************
*	请求一个通道
*******************************************************************************/
char bsp_pdma_alloc(void)
{
	char ch;
	
	intx_alloc();	
	
	intx_disable();
	
	// 搜索空闲通道
	for(ch = 2; ch < PDMA_CH_MAX;ch ++)
	{
		if(used[ch] == 0)
		{
			used[ch] = 1;
			break;
		}
	}

	if(ch < 16)
	{
		// 请求成功
		PDMA->CHRST = 1UL << ch;
		PDMA->INTEN &= ~(1UL << ch);
		PDMA->CHRST = 0;
	}
	else
	{
		// 请求失败
		ch = -1;
	}
	
	intx_enable();	
	
	return ch;
}

/*******************************************************************************
*	软件触发
*******************************************************************************/
void bsp_pdma_sw_tig(char ch)
{
	// bit[x] = 0无效
	PDMA->SWREQ = 1UL << ch;
}

/*******************************************************************************
*	释放一个通道
*******************************************************************************/
char bsp_pdma_free(char ch)
{
	intx_alloc();	
	
	// 清中断状态
	bsp_pdma_intsts_clr(ch);	
	
	// 复位其他状态
	intx_disable();
	
	PDMA->CHRST = 1UL << ch;
	
	isr_callback[ch] = 0;
	used[ch] = 0;	
	
	PDMA->CHRST = 0;
	
	intx_enable();	
	
	return ch;
}

/*******************************************************************************
* config , 返回通道号
*******************************************************************************/
void bsp_pdma_cfg(const pdma_basic_cfg_type *cfg, char ch)
{
	char src_pos;
	uint32_t ctr;
	
	intx_alloc();
	
	// ctr mode
	ctr = PDMA_OP_BASIC;
	ctr |= cfg->ctr_da_mode;
	ctr |= cfg->ctr_sa_mode;
	ctr |= cfg->ctr_txt_mode;
	ctr |= cfg->ctr_width;
	ctr |= cfg->ctr_amount << PDMA_DSCT_CTL_TXCNT_Pos;
	
	// prio
	bsp_pdma_prio(ch, cfg->prio);
	bsp_pdma_int_en(ch, cfg->int_en);
	
	// config
	PDMA->DSCT[ch].DA = cfg->da;
	PDMA->DSCT[ch].SA = cfg->sa;
	PDMA->DSCT[ch].NEXT = 0;
	
	// software fun
	isr_callback[ch] = cfg->call_back;
	
	src_pos = (ch & 3) * 8;
	
	// enable
	intx_disable();
	
	(&PDMA->REQSEL0_3)[ch>>2] = ((&PDMA->REQSEL0_3)[ch>>2] & (0xFFUL << src_pos) ) | (cfg->req_src << src_pos);
	
	PDMA->DSCT[ch].CTL = ctr;
	
	PDMA->CHCTL |= 1UL << ch;
	
	intx_enable();
}

